package com.ruoyi.iot.service.impl;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.iot.domain.IotQueryResult;
import com.ruoyi.iot.service.IotQueryService;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.iotdb.isession.ITableSession;
import org.apache.iotdb.isession.SessionDataSet;
import org.apache.iotdb.isession.pool.ITableSessionPool;
import org.apache.iotdb.rpc.IoTDBConnectionException;
import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.tsfile.read.common.Field;
import org.apache.tsfile.read.common.RowRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class IotQueryServiceImpl implements IotQueryService {

    private static final Logger log = LoggerFactory.getLogger(IotQueryServiceImpl.class);

    private final ITableSessionPool sessionPool;

    public IotQueryServiceImpl(ITableSessionPool sessionPool) {
        this.sessionPool = sessionPool;
    }

    @Override
    public IotQueryResult executeQuery(String sql) {
        if (StringUtils.isBlank(sql)) {
            throw new ServiceException("SQL statement must not be empty");
        }
        String formattedSql = normalizeSql(sql);
        try (ITableSession session = sessionPool.getSession();
             SessionDataSet dataSet = session.executeQueryStatement(formattedSql)) {

            List<String> columns = dataSet.getColumnNames();
            List<String> columnTypes = dataSet.getColumnTypes();
            List<Map<String, Object>> rows = new ArrayList<>();
            while (dataSet.hasNext()) {
                RowRecord record = dataSet.next();
                rows.add(mapRow(record, columns, columnTypes));
            }
            return new IotQueryResult(columns, columnTypes, rows);
        } catch (IoTDBConnectionException | StatementExecutionException ex) {
            log.error("Failed to execute IoTDB query, sql={}", sql, ex);
            throw new ServiceException("Failed to execute IoTDB query: " + ex.getMessage())
                .setDetailMessage(ex.toString());
        }
    }

    private Map<String, Object> mapRow(RowRecord record, List<String> columns, List<String> columnTypes) {
        List<Field> fields = record.getFields();
        Map<String, Object> row = new LinkedHashMap<>(columns.size());
        for (int i = 0; i < columns.size(); i++) {
            String columnName = columns.get(i);
            String declaredType = columnTypes.size() > i ? columnTypes.get(i) : null;
            row.put(columnName, extractFieldValue(fields, i, declaredType));
        }
        return row;
    }

    private Object extractFieldValue(List<Field> fields, int index, String declaredType) {
        if (fields == null || index >= fields.size()) {
            return null;
        }
        Field field = fields.get(index);
        if (field == null || field.getDataType() == null) {
            return null;
        }

        String normalizedType = normalizeTypeName(declaredType, field);
        switch (normalizedType) {
            case "BOOLEAN":
                return field.getBoolV();
            case "INT8":
            case "INT16":
            case "INT32":
            case "INT":
            case "INTEGER":
                return field.getIntV();
            case "INT64":
            case "LONG":
            case "BIGINT":
            case "TIMESTAMP":
                return field.getLongV();
            case "FLOAT":
                return field.getFloatV();
            case "DOUBLE":
                return field.getDoubleV();
            case "TEXT":
            case "STRING":
            case "VARCHAR":
            case "BLOB":
                return field.getStringValue();
            default:
                try {
                    return field.getStringValue();
                } catch (Exception ex) {
                    log.debug("Unable to convert IoTDB field value, index={}, type={}", index, normalizedType, ex);
                    return null;
                }
        }
    }

    private String normalizeTypeName(String declaredType, Field field) {
        String resolved = declaredType;
        if (StringUtils.isBlank(resolved) && field.getDataType() != null) {
            resolved = field.getDataType().toString();
        }
        if (resolved == null) {
            return "";
        }
        return resolved.trim().toUpperCase(Locale.ROOT);
    }

    private String normalizeSql(String sql) {
        String trimmed = sql.trim();
        if (trimmed.endsWith(";")) {
            return trimmed.substring(0, trimmed.length() - 1);
        }
        return trimmed;
    }
}
